﻿using MongoDB.Driver;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Entities.ScreeningTool;
using Sustainalytics.ScreeningTool.Synchronization.Caches;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Sustainalytics.ScreeningTool.Synchronization
{
    public class StandardFieldsCreator
    {
        private readonly ResearchStatesCache _researchStatesCache;
        private readonly ExcludedCompaniesCache _excludedCompaniesCache;
        private readonly IMongoCollection<CorporateData> _corporateDataCollection;
        private readonly IMongoCollection<CompanyProfileResearchState> _companyProfileResearchStateCollection;
        private readonly IMongoCollection<ScreeningData> _screeningDataCollection;

        public StandardFieldsCreator(
            ResearchStatesCache researchStatesCache,
            ExcludedCompaniesCache excludedCompaniesCache,
            IMongoCollection<CorporateData> corporateDataCollection,
            IMongoCollection<CompanyProfileResearchState> companyProfileResearchStateCollection,
            IMongoCollection<ScreeningData> screeningDataCollection)
        {
            _researchStatesCache = researchStatesCache;
            _excludedCompaniesCache = excludedCompaniesCache;
            _corporateDataCollection = corporateDataCollection;
            _companyProfileResearchStateCollection = companyProfileResearchStateCollection;
            _screeningDataCollection = screeningDataCollection;
        }

        public async Task AddStandardFieldsAsync(ClientFacingProduct clientFacingProduct)
        {
            var filter = Builders<CorporateData>.Filter.Nin(x => x.Id, _excludedCompaniesCache);

            await _corporateDataCollection
                .Find(filter)
                .Project(GetScreeningDataProjection(clientFacingProduct))
                .ForEachAsync(async screeningData =>
                {
                    await _screeningDataCollection.InsertOneAsync(screeningData).ConfigureAwait(false);
                })
                .ConfigureAwait(false);
        }

        private Expression<Func<CorporateData, ScreeningData>> GetScreeningDataProjection(ClientFacingProduct clientFacingProduct)
            => clientFacingProduct.IsGA() ? ScreeningDataProjectionForGA() : ScreeningDataProjectionForDS();

        private Expression<Func<CorporateData, ScreeningData>> ScreeningDataProjectionForGA()
        {
            return corporateData => new ScreeningData
            {
                Id = corporateData.Id,
                CompanyName = corporateData.CompanyName,
                CompanyNameLowerCase = corporateData.CompanyNameLowercase,
                CompanyNameUrlFriendly = corporateData.CompanyNameUrlFriendly,
                MainExchange = corporateData.MainExchange,
                MainTicker = corporateData.MainTicker,
                CompanyId = corporateData.CapitalIqId,
                TemplateType = corporateData.TemplateType,
                ResearchParentId = corporateData.ResearchParentId,
                ResearchStates = _researchStatesCache.GetResearchStates(corporateData.ResearchParentId ?? corporateData.Id),
                UniverseIds = corporateData.UniverseIds,
            };
        }

        private Expression<Func<CorporateData, ScreeningData>> ScreeningDataProjectionForDS()
        {
            return corporateData => new ScreeningData
            {
                Id = corporateData.Id,
                CompanyId = corporateData.CapitalIqId,
                CompanyName = corporateData.CompanyName,
                TemplateType = corporateData.TemplateType,
                ResearchParentId = corporateData.ResearchParentId,
                ResearchStates = _researchStatesCache.GetResearchStates(corporateData.ResearchParentId ?? corporateData.Id),
                SecurityListIds = new List<string>(),
            };
        }

        public async Task AddSecurityListIdsAsync(ClientFacingProduct clientFacingProduct)
        {
            if (clientFacingProduct.IsGA())
                return;

            await _companyProfileResearchStateCollection
                .Find(x => x.SecurityListIds != null)
                .ForEachAsync(async companyProfile =>
                {
                    var updateDefinition = Builders<ScreeningData>.Update.Set(x => x.SecurityListIds, companyProfile.SecurityListIds);
                    await _screeningDataCollection.UpdateOneAsync(x => x.Id == companyProfile.Id, updateDefinition);
                }).ConfigureAwait(false);
        }
    }
}
